/******************************交通灯显示********************************
 *Id:   21********
 *Coder:芒果酱
 *Time: 2024.4.26
 ***********************************************************************/

#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "stdlib.h"
#include "includes.h"
#define uchar unsigned char

// 设置任务堆栈大小
#define LED_STK_SIZE    64
#define TIME_STK_SIZE   64
#define KEY_STK_SIZE    64
#define SERIAL_STK_SIZE 64
#define START_STK_SIZE 256

// 设置任务优先级
#define LED_TASK_Prio    1
#define TIME_TASK_Prio   2
#define KEY_TASK_Prio    3
#define SERIAL_TASK_Prio 4
#define START_TASK_Prio 10

// 任务堆栈
OS_STK TASK_TIME_STK[TIME_STK_SIZE];
OS_STK TASK_LED_STK[LED_STK_SIZE];
OS_STK TASK_KEY_STK[KEY_STK_SIZE];
OS_STK TASK_SERIAL_STK[SERIAL_STK_SIZE];
OS_STK TASK_START_STK[START_STK_SIZE];

//-----------------------函数定义----------------------------
void ClearShow(int start, int end);
void InitData(void);
void ModeNowShow(void);
void InitBand(void);
void SettingMode(void);
void ShowNode(void);
char *GetStrInfo(void);
void TrafficLightControl(void);
void TaskStart(void *pdata);
// 任务申明
void TaskLed(void *pdata);
void TaskTime(void *pdata);
void TaskKey(void *pdata);
void TaskSerial(void *pdata);
// 系统时钟配置函数
void SysTick_Configuration(void);

//-----------------------物理量---------------------------
// 数码管当前存储(10为不显示)
int NodeShow[8] = {10, 1, 10, 10, 10, 1, 10, 10};
// 按键当前值(keyup赋值1，keyudowm赋值-1)
int keyup = 0, keydown = 0;

//-----------------------配置表----------------------------
// 红绿灯文字串口输出
char *ledstr[] = {
	"Green",
	"Yellow",
	"Red",
};

// 0表示绿灯 1示黄灯 2表示红灯
int A_lendChangeconfig[] = {0, 1, 2}, // A灯显示顺序表
	B_lendChangeconfig[] = {2, 0, 1}, // B灯显示顺序表
	// 两个数码管显示的什么时间(0绿灯 1黄灯 2红灯)，这里表示lendChangeconfig数组的位置
	A_LedNowLocal = 0, // 存储位置(这里0不代表绿灯，只是数组位置，范围0-2)
	B_LedNowLocal = 0, // 存储位置(这里0不代表绿灯，只是数组位置，范围0-2)
	// 注意：绿灯 + 黄灯= 红灯 15s+5s=20s
	LedTime[] = {15, 5, 20},
	// 0绿灯‘L’字符, 1黄灯‘F’字符 ,2红灯‘H’字符
	ShowType[] = {20, 17, 18},
	// 两个数码管当前显示的时间s
	A_LedNowTime = 0,
	B_LedNowTime = 0,
	// 两个数码管当前显示的时间是否闪
	A_Ledflashing = 0,
	B_Ledflashing = 0,
	// 闪烁开始时间s
	FlashingTime = 5,
	// 设置模式判断0为显示模式,1为设置绿灯,2为设置黄灯，3为设置红灯(这里自动计算，无法直接设置)
	ModeNow = 0;

//-------------------------中间变量--------------------------------------
u8 t, len;      //串口长度
long times = 0; //计数
//--------------------------系统时钟中断服务相关函数--------------------------------------
// 系统时钟中断服务函数
void SysTick_Handler(void)
{
	OS_ENTER_CRITICAL(); /* Tell uC/OS-II that we are starting an ISR  */
	OSIntNesting++;
	OS_EXIT_CRITICAL();
	OSTimeTick(); /* Call uC/OS-II's OSTimeTick()               */
	OSIntExit();  /* Tell uC/OS-II that we are leaving the ISR  */
}

// 系统时钟配置，设计1ms产生一次中断
void SysTick_Configuration(void)
{
	SysTick->CTRL &= ~(1 << 2); // SYSTICK使用外部时钟源
	SysTick->CTRL |= 1 << 1;	// 开启SYSTICK中断
	SysTick->LOAD = 9000;		// 产生1ms中断
	// bit2清空,选择外部时钟  HCLK/8
	// MY_NVIC_Init(3,3,SystemHandler_SysTick,2);//组2，最低优先级
	SysTick->CTRL |= 1 << 0; // 开启SYSTICK
}
//-----------------------运行模式相关函数----------------------------
// 模式选择函数
void ModeNowShow()
{
	if (ModeNow == 0)
	{
		// 红绿灯模式
		TrafficLightControl();
	}
	else
	{
		// 设置模式
		SettingMode();
	}
}

// 红绿灯控制与显示函数
void TrafficLightControl()
{
	int temptime = times % 100;
	// 闪烁显示，这里可以控制闪烁速度
	if (A_Ledflashing == 1 && (temptime > 0 && temptime < 50))
	{
		// 不显示
		NodeShow[1] = 10;
		NodeShow[0] = 10;
	}
	else
	{
		// 第一个灯显示
		NodeShow[0] = A_LedNowTime / 10;
		if (NodeShow[0] == 0)
		{
			NodeShow[0] = 10; // 0限制不显示
		}
		NodeShow[1] = A_LedNowTime % 10;
	}
	// 闪烁显示，这里可以控制闪烁速度
	if (B_Ledflashing == 1 && (temptime > 0 && temptime < 50))
	{
		// 不显示
		NodeShow[6] = 10;
		NodeShow[7] = 10;
	}
	else
	{
		// 第二个灯显示
		NodeShow[6] = B_LedNowTime / 10;
		if (NodeShow[6] == 0)
		{
			NodeShow[6] = 10; // 0限制不显示
		}
		NodeShow[7] = B_LedNowTime % 10;
	}
	// 如果两个灯到了0，进入下一个灯的显示(如：绿灯->黄灯)
	if (A_LedNowTime == 0)
	{
		// 下一个灯
		A_LedNowLocal = (++A_LedNowLocal) % 3;
		// 下一个灯的时间
		A_LedNowTime = LedTime[A_lendChangeconfig[A_LedNowLocal]];
	}
	if (B_LedNowTime == 0)
	{
		// 下一个灯
		B_LedNowLocal = (++B_LedNowLocal) % 3;
		// 下一个灯的时间
		B_LedNowTime = LedTime[B_lendChangeconfig[B_LedNowLocal]];
	}
	// AB闪烁控制判断
	if (A_LedNowTime <= FlashingTime)
		A_Ledflashing = 1;
	else
		A_Ledflashing = 0;

	if (B_LedNowTime <= FlashingTime)
		B_Ledflashing = 1;
	else
		B_Ledflashing = 0;

	// 显示灯的类别
	NodeShow[3] = ShowType[A_lendChangeconfig[A_LedNowLocal]];
	NodeShow[4] = ShowType[B_lendChangeconfig[B_LedNowLocal]];
	// 不显示2、5数码管
	NodeShow[2] = 10;
	NodeShow[5] = 10;
}

// 设置模式函数
void SettingMode()
{
	// '设置数字'存储变量
	int SetTime01 = 0,
		SetTime02 = 0;
	// 自动计算红灯时间
	LedTime[2] = LedTime[0] + LedTime[1];
	// 计算每个位的数
	SetTime01 = LedTime[ModeNow - 1] / 10;
	SetTime02 = LedTime[ModeNow - 1] % 10;
	// 1为设置绿灯
	if (ModeNow == 1)
	{
		// 显示字母Ld
		NodeShow[0] = 20;
		NodeShow[1] = 15;
	}
	// 2为设置黄灯
	if (ModeNow == 2)
	{
		// 显示字母Fd
		NodeShow[0] = 17;
		NodeShow[1] = 15;
	}
	// 3为设置红灯
	if (ModeNow == 3)
	{
		// 显示字母Hd
		NodeShow[0] = 18;
		NodeShow[1] = 15;
	}
	// 显示‘-’
	NodeShow[2] = 28;
	NodeShow[3] = ModeNow;
	// 显示‘-’
	NodeShow[4] = 28;
	// 显示设置的数字
	NodeShow[5] = SetTime01;
	NodeShow[6] = SetTime02;
	// 按键控制,<3限制按键控制范围
	if (ModeNow < 3)
	{
		// 设置数据,>0
		if (LedTime[ModeNow - 1] > 1)
		{
			//+keydowm(keydowm==-1)
			LedTime[ModeNow - 1] = LedTime[ModeNow - 1] + keydown;
			// 清除按键标志
			keydown = 0;
		}
		// 设置数据,<99
		if (LedTime[ModeNow - 1] < 99)
		{
			//+keyup(keyup==1)
			LedTime[ModeNow - 1] = LedTime[ModeNow - 1] + keyup;
			// 清除按键标志
			keyup = 0;
		}
	}
	NodeShow[7] = 10;
	// 更新显示数据
	InitData();
}

//-----------------------显示相关函数----------------------------
// 清除指定区域显示 1-8数码管内容函数
void ClearShow(int start, int end)
{
	int i = start - 1;
	for (; i < (end); i++)
	{
		// 用10去除原来的数据
		NodeShow[i] = 10;
	}
}

//-----------------------串口相关-----------------------------
// 串口字符串中的具体信息并且设置
void ConfigLEDShow(int i, int SetTime)
{
	switch (i)
	{
	case 1:
		LedTime[2] = SetTime;
		break;
	case 2:
		LedTime[0] = SetTime;
		break;
	case 3:
		LedTime[1] = SetTime;
		break;
	case 4:
		A_LedNowTime = SetTime;
		break;
	case 5:
		B_LedNowTime = SetTime;
		break;
	default:
		break;
	}
}

// 获取串口字符串中的具体信息
char *GetStrInfo()
{
	// 变量定义
	int num = 0, NumLength = len - 3;
	char str[NumLength], temp;
	int i = 0;
	// 长度不够判断
	if (len <= 3)
		return "\nERROR:len too low";
	// 读取数据大小
	for (; i < NumLength; i++)
	{
		temp = USART_RX_BUF[3 + i];
		// 存在其他字符判断
		if (temp >= '0' && temp <= '9')
			str[i] = temp;
		else
			return "\nERROR:String have 'abc...'";
	}
	// char数组转int
	const char *ptr = str;
	while (*ptr)
	{
		num *= 10;
		num += *ptr - '0';
		ptr++;
	}
	// 配置程序
	ConfigLEDShow((USART_RX_BUF[1] - '0'), num);
	// 返回信息
	return "\nSucess";
}

// 输出串口信息
void PrintMsg()
{
	printf("\n\n----Now LED Config,your can input:\"(2)12\"----");
	printf("\n(1)Red Time(Can't Change):%d", LedTime[2]);
	printf("\n(2)Green Time:%d", LedTime[0]);
	printf("\n(3)Yellow Time:%d", LedTime[1]);
	printf("\n(4)NS Now Time:%d (Min:1,Max:%d,Type:%s)", A_LedNowTime, LedTime[A_lendChangeconfig[A_LedNowLocal]], ledstr[A_lendChangeconfig[A_LedNowLocal]]);
	printf("\n(5)WE Now Time:%d (Min:1,Max:%d,Type:%s)", B_LedNowTime, LedTime[B_lendChangeconfig[B_LedNowLocal]], ledstr[B_lendChangeconfig[B_LedNowLocal]]);
	printf("\n------------------------------\n");
}

// 串口函数
void Serial()
{

	if (USART_RX_STA & 0x80) // 7位STA0-6数据，7-8标志与长度
	{
		len = USART_RX_STA & 0x3f; // 得到此次接收到的数据长度,6位
		// 输出结果
		printf(GetStrInfo());
		printf(" : ");
		// 返回结果
		for (t = 0; t < len; t++)
		{
			USART1->DR = USART_RX_BUF[t];
			while ((USART1->SR & 0X40) == 0)
				; // 等待发送结束
		}
		printf("\n"); // 插入换行
		len = 0;
		PrintMsg();
		USART_RX_STA = 0;
	}
	else
	{
	   PrintMsg();
	}
}

//-----------------------初始化相关函数----------------------------
// 板子初始化函数
void InitBand()
{
	// 系统频率
	Stm32_Clock_Init(6);
	// 延时启动
	delay_init(72);
	// 串口初始化为9600
	uart_init(72, 9600);
	// led初始化
	LED_Init();
	// 按键初始化
	KEY_Init();
	// 屏蔽LED
	LED_SEL = 0;
}

// 数据初始化函数
void InitData()
{
	// 显示归位
	A_LedNowLocal = 0;
	B_LedNowLocal = 0;
	// 计算初始的时间
	A_LedNowTime = LedTime[A_lendChangeconfig[A_LedNowLocal]];
	B_LedNowTime = LedTime[B_lendChangeconfig[B_LedNowLocal]];
}

//-----------------------主函数----------------------------
int main()
{
	// 板子初始化
	InitBand();
	// 数据初始化
	InitData();
	// 定时器
	SysTick_Configuration();
	//任务初始化
	OSInit();
	//初始化任务
	OSTaskCreate(TaskStart,										// task 函数
				 (void *)0,										// parameter传递参数
				 (OS_STK *)&TASK_START_STK[START_STK_SIZE - 1], // task stack top pointer
				 START_TASK_Prio);								// task 优先级
	//开始任务
	OSStart();
	return 0;
}
//-----------------------任务相关函数----------------------------
// 开始任务
void TaskStart(void *pdata)
{
	pdata = pdata;
	OS_ENTER_CRITICAL();
	OSTaskCreate(TaskLed, (void *)0, (OS_STK *)&TASK_LED_STK[LED_STK_SIZE - 1], LED_TASK_Prio);
	OSTaskCreate(TaskTime, (void *)0, (OS_STK *)&TASK_TIME_STK[TIME_STK_SIZE - 1], TIME_TASK_Prio);
	OSTaskCreate(TaskKey, (void *)0, (OS_STK *)&TASK_KEY_STK[KEY_STK_SIZE - 1], KEY_TASK_Prio);
	OSTaskCreate(TaskSerial, (void *)0, (OS_STK *)&TASK_SERIAL_STK[SERIAL_STK_SIZE - 1], SERIAL_TASK_Prio);
	OSTaskSuspend(START_TASK_Prio); // suspend but not delete
	OS_EXIT_CRITICAL();
}

// 任务1
// 显示数码管
void TaskLed(void *pdata)
{
	// 变量定义
	uchar i, icode;
	// 屏蔽LED
	LED_SEL = 0;
	while (TRUE)
	{
		//闪烁计数
		if (times==1000)
		{
			times = 0;
		}
		times++;
		// 当前模式控制显示
		ModeNowShow();
		// 8位显示
		for (i = 0; i < 8; i++)
		{
			// 第i位数码管显示数字
			icode = NodeShow[i];
			// 显示数字
			SetLed(i, icode);
			//1ms 频率控制(显示亮度控制,数值越大显示越亮)
			OSTimeDlyHMSM(0, 0, 0, 1);
		}
	}
}

// 任务2
// 倒计时.
void TaskTime(void *pdata)
{
	while (TRUE)
	{
		OS_ENTER_CRITICAL();
		// AB倒计时
		if (A_LedNowTime != 0)
			A_LedNowTime--;
		if (B_LedNowTime != 0)
			B_LedNowTime--;
		OS_EXIT_CRITICAL();
		//一秒钟倒计时
		OSTimeDlyHMSM(0, 0, 1, 0);
	}
}

// 任务3
// 按键控制
void TaskKey(void *pdata)
{
	while (TRUE)
	{
		if (KEY1 == 0)
		{
			keyup = 1; // 设置keyup标志
		}
		if (KEY2 == 0)
		{
			keydown = -1; // 设置keydown标志
		}
		if (KEY3 == 0)
		{
			// 进入设置
			ModeNow = (++ModeNow) % 4;
			// 清除缓存区显示(1-8数码管内容)
			ClearShow(1, 8);
		}
		//防抖延时
		OSTimeDlyHMSM(0, 0, 0, 120);
	}
}
// 任务4
// 倒计时.
void TaskSerial(void *pdata)
{
	while (TRUE)
	{
		//倒计时
		OSTimeDlyHMSM(0, 0, 2, 0);
		// 串口配置函数
		Serial();
	}
}

//--------------------使用教程---------------------------------
// 最左最右的数码管表示两个方向的倒计时，中间为灯的类型
// LD绿灯‘L’字符, FD黄灯‘F’字符 ,HD红灯‘H’字符
// 按键1进入设置,按键2设置减小,按键3设置增加，跳出即可设置完成
 
//---------------------附件-----------------------------
/*
导入下面这里的库文件，学校提供
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "usart.h"
#include "key.h"
(重要)注意这里要修改led.c变量segTable为
 u8 segTable[] =
	0x3f,//0
	0x06,//1
	0x5b,//2
	0x4f,
	0x66,//数字4
	0x6d,
	0x7d,
	0x07,
	0x7f,
	0x6f,
	0x00,
	0x77,
	0x7C,
	0x39,
	0x58,
	0x5E,
	0x79,
	0x71,
	0x76,
	0x74,
	0x38,
	0x54,
	0x37,
	0x5C,
	0x73,
	0x50,
	0x78,
	0x3E,
	0x40
*/


